// ES5 近类结构
// 类的特性：封装、继承、多态
// 封装：构造函数的定义，this对象代表属性，内部函数为方法
// 多态：依靠参数传递实例特性
// 继承：原型对象
// function Person(name) {
//   this.name = name;
// }

// Person.prototype.sayHello = function () {
//   console.log('Hi,' + this.name)
// }

// let person = new Person('wgf')

// person.sayHello()

// -------------------------------------------
// 类的声明
// 关键字 class + 类名 + 类似对象字面量（属性间不用逗号隔开）

// class PersonClass {
//   constructor(name) {
//     this.name = name;
//   }

//   sayHello() {
//     console.log('Hi,' + this.name);
//   }
// }

// PersonClass.sayHello = function () {
//   this.name = 'asdfa'
// }

// let person = new PersonClass('Bill')
// person.sayHello()

// // console.log(person instanceof PersonClass)
// // console.log(person instanceof Object)
// // console.log(PersonClass.prototype.sayHello.toString())

// console.log(person, person.hasOwnProperty())
// console.log(typeof PersonClass.prototype.sayHello)
// -----------------------------------------------------------
/**
 * 类与自定义类型区别
 *
 * 1. 函数声明能提升，但类声明和let一样，会出现暂时性死区
 * 2. 类声明内代码自动运行在严格模式内，且无法脱离
 * 3. 自定义类型需要手动 Object.defineProperty() 设置方法不可枚举；而类中所有方法不可枚举
 * 4. 内个类都存在内部方法 [[Contructor]], 关键字 new 需要调用该方法
 * 5. 使用除 new 以外方式调用类的构造函数报错
 * 6. 类中修改类名报错
 */

// let PersonType2 = (function () {
//   'user strict'

//   const PersonType2 = function (name) {
//     // 确保通过 new 调用该函数
//     /**
//      * ES6 new.target
//      * 用于检测构造函数或者函数是否由 关键字new 调用
//      * true：指向构造函数或者函数的引用
//      * false：返回 ‘undefined’
//      */
//     if (typeof new.target === 'undefined') {
//       throw new Error("必须通过关键字 new 调用·1")
//     }
//     this.name = name
//   }

//   Object.defineProperty(PersonType2.prototype, 'sayHello', {
//     value: function () {
//       if (typeof new.target !== 'undefined') {
//         throw new Error('该函数不能用 new 调用')
//       }

//       console.log('Hi,' + this.name)
//     },
//     enumerable: false,
//     writable: false,
//     configurable: true
//   })
// })

// ---------------------------------------------
// 常量类名
// 类名无法在类的内部进行修改，但是外部可以（类似于 let 变量）
// class Foo {
//   constructor(name) {
//     this.name = name
//   }
// }

// Foo = 'bar'

// console.log(Foo)

// ----------------------------------------------------
// 类表达式
// 基础（匿名）

// let PersonClass = class {
//   constructor(name) {
//     this.name = name
//   }

//   sayHello() {
//     console.log('Hello,' + this.name)
//   }
// }
// ----------------------------------------------------
// 命名表达式

// let PersonClass = class PersonClass2 {
//   constructor(name) {
//     this.name = name
//   }

//   sayHello() {
//     this.type = new PersonClass2('test')
//     console.log(this.type.name)
//   }
// }

// const person = new PersonClass('wgf')
// person.sayHello()

// // 等价上述代码
// let PersonClassFn = (function () {
//   'use strict'

//   const PersonClassFn2 = function (name) {
//     if (typeof new.target === 'undefined') {
//       throw new Error('must use “new” world')
//     }
//     this.name = name
//   }

//   Object.defineProperty(PersonClassFn2.prototype, 'sayHello', {
//     value: function () {
//       if (typeof new.target !== 'undefined') {
//         throw new Error('Don\'t use "new" world')
//       }
//       this.type = new PersonClassFn2('test')
//       console.log(this.type.name)
//     }
//   })

//   return PersonClassFn2
// })

// -----------------------------------------------------------
// 作为一等公民
/**
 * 1. 可以传入函数
 * 2. 从函数返回
 * 3. 赋值给变量
 */

// 作为参数
// function createObject(classDef) {
//   return new classDef();
// }

// let obj = createObject(class {
//   sayHello() {
//     console.log('Hi!')
//   }
// })

// obj.sayHello()


// 利用 立即执行函数 创建 单例
// let person = new class {
//   constructor(name) {
//     this.name = name
//   }
//   sayName() {
//     console.log(this.name)
//   }
// }('Mike')
// console.log(person)

// ---------------------------------------------
// 访问器属性

// class CustomHTMLElement {
//   constructor(element) {
//     this.element = element
//   }

//   get html() {
//     return this.element.innerHTML
//   }

//   set html(value) {
//     this.element.innerHTML = value
//   }
// }

// const element = new CustomHTMLElement(document.getElementById('app'))
// let key = 0
// const obj = Object.defineProperty({}, 'html', {
//   get() {
//     return key
//   },
//   set(value) {
//     key = value
//   }
// })

// console.log(element, obj)

// --------------------------------------------------------
// 可计算成员名称
// let methods = 'sayHello'
// class Person {
//   constructor(name) {
//     this.name = name
//   }

//   [methods]() {
//     console.log(this.name)
//   }
// }

// let person = new Person('wgf')
// person['sayHello']()
// methods = 'test'
// person['test']()

// ----------------------------------------------------
// 生成器方法
// class MyClass {
//   *createIterator() {
//     yield 1;
//     yield 2;
//     yield 3;
//   }
// }

// class Collection {
//   constructor() {
//     this.items = []
//   }

//   *[Symbol.iterator]() {
//     yield* this.items.values() // 委托生成迭代器
//   }
// }

// let collection = new Collection()
// collection.items.push(1)
// collection.items.push(2)
// collection.items.push(3)
// collection.items.push(5)

// for (let value of collection) {
//   console.log(value)
// }

// ------------------------------------------------------
// 静态成员
// ES5 会在构造函数上添加属性
function Person(name) {
  this.name = name
}

Person.create = function (name) {
  return new Person(name)
}

const person = Person.create('we')
console.log(person)

// ES6 中在方法名前 加 static 即可
class PersonClass {
  constructor(name) {
    this.name = name
  }

  static createPerson(name) {
    return new PersonClass(name)
  }
}

const person_2 = PersonClass.createPerson('wer')
console.log(person_2)


